home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1996-09-17 | 24.2 KB | 755 lines | [ TEXT/MPS ]
//======================================================================================== // // File: FWExcImp.cpp // Release Version: $ ODF 2 $ // // Copyright: (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved. // //======================================================================================== #ifndef FW_NATIVE_EXCEPTIONS #include "FWFound.hpp" #ifndef FWEXCIMP_H #include "FWExcImp.h" #endif #ifndef FWDEBUG_H #include "FWDebug.h" #endif #ifdef FW_BUILD_MAC #include <Gestalt.h> #endif #ifdef FW_BUILD_MAC #pragma segment slexcept #endif #ifdef FW_DEBUG #include <stdio.h> #define _FW_NAME_PARAMETER ,char* name #else #define _FW_NAME_PARAMETER #endif //======================================================================================== // struct FW_SExceptionGlobals //======================================================================================== struct FW_SExceptionGlobals { FW_SPrivTryBlockContext* fCurrentContext; FW_SPrivWatcher* fWatchList; FW_SPrivDeleteElem* fStackBottom; // These are pointers into the delete stack, FW_SPrivDeleteElem* fStackTop; // not pointers into the function call stack FW_SPrivDeleteElem* fStackLimit; void* fExceptionBuffer; size_t fExceptionBufferSize; FW_SPrivExceptionInfo fCaughtException; FW_SPrivExceptionInfo fThrownException; FW_Boolean fIsUnwinding; void* fCallStackBase; void* fProcessStackBase; FW_PlatformThreadID fThreadID; }; static FW_SExceptionGlobals gMainThreadExceptionData = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static FW_SExceptionGlobals* gExceptionData = &gMainThreadExceptionData; // FW_gInStaticInit is initialized to a special magic number before static initialization // here. It should be cleared by the application main or shared library initialization // routine (CFMInit) immediately after static initialization has been performed, and before // the first try block or construction of an autodestruct object with auto scope or // dynamic scope. Note that this mechanism precludes placing // this exception emulation system in a shared library that other clients could use. // Currently, every shared library component must statically link in this eumulation // system. However, we did have this system working from a shared library, and the // use of FW_gInStaticInit is the only thing that we have done since then that would // prevent it from moved back into a shared library. [JEL, ODF R1, 3/21/96] const long kMagicCookie = 'jiml'; long FW_gInStaticInit = kMagicCookie; void* FW_gPrivVolatileKludge = 0; //======================================================================================== // Key Utility Functions //======================================================================================== static FW_Boolean FW_PrivHasThreadManager() { static FW_Boolean gHasFeature = FALSE; #ifdef FW_BUILD_MAC static FW_Boolean gChecked = FALSE; if (!gChecked) { long response; gHasFeature = ((Gestalt(gestaltThreadMgrAttr, &response) == noErr) && (response & (1 << gestaltThreadMgrPresent))); gHasFeature &= (&GetCurrentThread != (void*)kUnresolvedCFragSymbolAddress); gChecked = TRUE; } #endif return gHasFeature; } static FW_PlatformThreadID FW_PrivGetCurrentThread() { FW_PlatformThreadID currentThread = 0; #ifdef FW_BUILD_MAC if (FW_PrivHasThreadManager()) { FW_PlatformError err = GetCurrentThread(¤tThread); if (err != FW_xNoError) { FW_DEBUG_MESSAGE("FWExcImp::FW_PrivGetCurrentThread failed"); currentThread = 0; } } #endif return currentThread; } static long AddressIsOnStack(void* p) { long local; FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fCurrentContext); return (p > &local) && p <= gExceptionData->fCallStackBase; } static long IsCurrentSubObject(void* p) { register FW_SPrivWatcher* w = gExceptionData->fWatchList; return w && p>=w->fObject && p<w->fLimit; } static long IsCurrentObject(void* p, void* limit) { register FW_SPrivWatcher* w = gExceptionData->fWatchList; return w && p==w->fObject && limit==w->fLimit; } static long TrackThisObject(void* object) { FW_PRIV_ASSERT(gExceptionData->fCurrentContext); return AddressIsOnStack(object) || IsCurrentSubObject(object); } static void DoTerminate() { // If we get here, we either have a throw with no try block in scope, // or a rethrow with no exception in scope. extern void terminate(); FW_DEBUG_MESSAGE("Exception error, calling terminate()"); #if defined(__SC__) || defined(__MRC__) // Symantec & MrC currently don't have terminate() in their runtime library. ExitToShell(); #else terminate(); #endif } static void InitializeExceptionGlobals(FW_SExceptionGlobals* globals, void* bottom) { const int kInitialStackSize = 1000; const int kExceptionBufferSize = 256; globals->fProcessStackBase = LMGetCurStackBase(); globals->fCallStackBase = (globals == &gMainThreadExceptionData) ? globals->fProcessStackBase : bottom; globals->fThreadID = FW_PrivGetCurrentThread(); globals->fStackTop = globals->fStackBottom = (FW_SPrivDeleteElem*)FW_PrimitiveAllocateBlock(kInitialStackSize*sizeof(FW_SPrivDeleteElem)); globals->fStackLimit = globals->fStackBottom + kInitialStackSize; globals->fExceptionBuffer = FW_PrimitiveAllocateBlock(kExceptionBufferSize); globals->fExceptionBufferSize = kExceptionBufferSize; } static void FinalizeExceptionGlobals(FW_SExceptionGlobals* globals) { FW_PrimitiveFreeBlock(globals->fStackBottom); globals->fStackTop = globals->fStackBottom = 0; globals->fThreadID = 0; FW_PrimitiveFreeBlock(globals->fExceptionBuffer); globals->fExceptionBuffer = 0; } //======================================================================================== // DeleteElement Functions //======================================================================================== static void DeleteElem_DestroyObject(FW_SPrivDeleteElem* elem) { FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fIsUnwinding); FW_PRIV_ASSERT(elem); FW_PRIV_ASSERT(elem->fDestroyer); FW_PRIV_ASSERT(elem->fObject); (*elem->fDestroyer)(elem->fObject); FW_PRIV_ASSERT(!elem->fObject); } //======================================================================================== // ExceptionInfo Functions //======================================================================================== static void ExceptionInfo_Clear(FW_SPrivExceptionInfo& exception) { exception.fAddress = 0; // The remainder don't have to be cleared, but we do so anyway exception.fSize = 0; exception.fClass = 0; exception.fDestroyer = 0; exception.fCloner = 0; } static void ExceptionInfo_DestroyException(FW_SPrivExceptionInfo& exception) { FW_PRIV_ASSERT(exception.fDestroyer); FW_PRIV_ASSERT(exception.fAddress); (*exception.fDestroyer)(exception.fAddress); ExceptionInfo_Clear(exception); } static void ExceptionInfo_Set(FW_SPrivExceptionInfo& exception, void* address, size_t size, FW_SClassInfoPtr classPtr, FW_PrivDestroyProc destroyer, FW_PrivCloneProc cloner) { exception.fAddress = address; exception.fSize = size; exception.fClass = classPtr; exception.fDestroyer = destroyer; exception.fCloner = cloner; } //======================================================================================== // DeleteStack Functions //======================================================================================== static void DeleteStack_ResetTop() { FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fStackTop); FW_PRIV_ASSERT(gExceptionData->fStackBottom); FW_PRIV_ASSERT(gExceptionData->fCurrentContext); FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1; FW_SPrivDeleteElem* last = gExceptionData->fStackBottom + gExceptionData->fCurrentContext->fDeleteStackLevel; while (elem>=last && !elem->fObject) --elem; gExceptionData->fStackTop = elem+1; } static void DeleteStack_Push(void* object, FW_PrivDestroyProc destroyer _FW_NAME_PARAMETER) { FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fStackBottom); FW_PRIV_ASSERT(gExceptionData->fStackTop); FW_PRIV_ASSERT(gExceptionData->fStackTop < gExceptionData->fStackLimit); gExceptionData->fStackTop->fObject = object; gExceptionData->fStackTop->fDestroyer = destroyer; #ifdef FW_DEBUG gExceptionData->fStackTop->fName = name; gExceptionData->fStackTop->fHeapAllocated = IsCurrentSubObject(object); if (gExceptionData->fStackTop->fHeapAllocated) { register FW_SPrivWatcher* w = gExceptionData->fWatchList; w->fDeleteElems++; } #endif gExceptionData->fStackTop++; } static void DeleteStack_ClearHeapObjectElements() { // Called as last step of FW_NEW FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fStackBottom); FW_PRIV_ASSERT(gExceptionData->fStackTop); FW_PRIV_ASSERT(gExceptionData->fStackTop < gExceptionData->fStackLimit); FW_PRIV_ASSERT(gExceptionData->fWatchList); FW_PRIV_ASSERT(gExceptionData->fCurrentContext); FW_SPrivWatcher* w = gExceptionData->fWatchList; FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1; FW_SPrivDeleteElem* last = gExceptionData->fStackBottom + gExceptionData->fCurrentContext->fDeleteStackLevel; while (elem>=last) { if (elem->fObject >= w->fObject && elem->fObject < w->fLimit) { FW_PRIV_ASSERT(elem->fHeapAllocated); elem->fObject = 0; #ifdef FW_DEBUG w->fDeleteElems--; #endif } --elem; } FW_PRIV_ASSERT(w->fDeleteElems == 0); DeleteStack_ResetTop(); } //======================================================================================== // TryBlockContext functions //======================================================================================== static void TryBlockContext_UnwindStack(FW_SPrivTryBlockContext* context) { FW_PRIV_ASSERT(context); FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fStackTop); FW_PRIV_ASSERT(gExceptionData->fStackBottom); gExceptionData->fIsUnwinding = true; FW_SPrivDeleteElem* last = gExceptionData->fStackBottom + context->fDeleteStackLevel; FW_PRIV_ASSERT(gExceptionData->fStackTop >= last); while (gExceptionData->fStackTop > last) { DeleteElem_DestroyObject(gExceptionData->fStackTop-1); // The destructor of the deleted object will have popped one or more elements // off of the stack so fStackTop will point to the new stack top. } FW_PRIV_ASSERT(gExceptionData->fStackTop == last); gExceptionData->fIsUnwinding = false; } inline void TryBlockContext_LongJump(FW_SPrivTryBlockContext* context) { (*context->fJumpProc)(*context->fJumpBuffer, 1); FW_PRIV_ASSERT(false); // should never return from above function } static char* gContextName = "TryBlockContext"; void FW_PrivTryBlockContext_Init(FW_SPrivTryBlockContext *self, jmp_buf buffer, FW_PrivLongJumpProc jumpProc) { if (!gExceptionData->fStackBottom) InitializeExceptionGlobals(gExceptionData, self); self->fPriorStackBase = gExceptionData->fCallStackBase; self->fPriorProcessBase = gExceptionData->fProcessStackBase; self->fPriorThreadID = gExceptionData->fThreadID; void* currentStackBase = LMGetCurStackBase(); FW_PlatformThreadID currentThread = FW_PrivGetCurrentThread(); if (currentStackBase != gExceptionData->fProcessStackBase || currentThread != gExceptionData->fThreadID) { // This code will execute in two cases: // 1. We've been called from a different process. This might happen during drag and drop. // 2. We've been called from a different thread and, consequently, stack-based object // will be created on a different stack. // // In the case of a different thread, LMGetCurStackBase returns the base of the stack // belonging to the primary execution thread - not the stack of the currently // executing thread. Consequently, we have to test both stackbase and current thread // to detect stack swaps. gExceptionData->fCallStackBase = self; gExceptionData->fProcessStackBase = currentStackBase; gExceptionData->fThreadID = currentThread; } self->fJumpBuffer = (jmp_buf *) buffer; self->fJumpProc = jumpProc; self->fDeleteStackLevel = gExceptionData->fStackTop - gExceptionData->fStackBottom; self->fPriorContext = FW_PrivTryBlockContext_MakeCurrent(self); #ifdef FW_DEBUG FW_AutoConstructed(self, sizeof(FW_SPrivTryBlockContext), FW_PrivTryBlockContext_Destroy, gContextName); #else FW_AutoConstructed(self, sizeof(FW_SPrivTryBlockContext), FW_PrivTryBlockContext_Destroy); #endif } void FW_PrivTryBlockContext_Destroy(void* context) { #ifdef FW_DEBUG FW_AutoDestructed(context, FW_PrivTryBlockContext_Destroy, gContextName); #else FW_AutoDestructed(context); #endif FW_SPrivTryBlockContext* self = (FW_SPrivTryBlockContext*) context; gExceptionData->fCallStackBase = self->fPriorStackBase; gExceptionData->fProcessStackBase = self->fPriorProcessBase; gExceptionData->fThreadID = self->fPriorThreadID; #ifdef FW_DEBUG while (gExceptionData->fStackTop != gExceptionData->fStackBottom+self->fDeleteStackLevel) { FW_SPrivDeleteElem* elem = --gExceptionData->fStackTop; if (elem->fObject != 0) { // If one of the above test fails, one or more objects were not removed off of // the delete stack by the time the try block went out of scope. This is almost // certainly because some class has an FW_END_CONSTRUCTOR without a matching FW_START_DESTRUCTOR. char message[256]; sprintf(message, "The autodestruct class %s is missing an FW_START_DESTRUCTOR", elem->fName); FW_DEBUG_MESSAGE(message); } } #endif FW_PRIV_ASSERT(self->fDeleteStackLevel == gExceptionData->fStackTop - gExceptionData->fStackBottom); FW_SPrivTryBlockContext* priorContext = FW_PrivTryBlockContext_MakeCurrent(self->fPriorContext); FW_PRIV_ASSERT(priorContext == self); } FW_SPrivTryBlockContext* FW_PrivTryBlockContext_MakeCurrent(FW_SPrivTryBlockContext* context) { FW_SPrivTryBlockContext *tmpContext = gExceptionData->fCurrentContext; gExceptionData->fCurrentContext = context; return tmpContext; } //======================================================================================== // FW_SPrivWatcher Functions //======================================================================================== static void Watcher_Push(FW_SPrivWatcher *self, void* obj, size_t size) { self->fObject = obj; self->fLimit = (char*)obj + size; self->fNext = gExceptionData->fWatchList; gExceptionData->fWatchList = self; } static char* gWatcherName = "Watcher"; void FW_PrivWatcher_Init(FW_SPrivWatcher* self, FW_PrivDeleteProc deleter) { self->fNext = 0; self->fObject = 0; self->fLimit = 0; self->fDeleter = deleter; #ifdef FW_DEBUG self->fDeleteElems = 0; FW_AutoConstructed(self, sizeof(FW_SPrivWatcher), FW_PrivWatcher_Destroy, gWatcherName); #else FW_AutoConstructed(self, sizeof(FW_SPrivWatcher), FW_PrivWatcher_Destroy); #endif } void* FW_PrivWatcher_Pop() { FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fWatchList); FW_PRIV_ASSERT(gExceptionData->fWatchList->fObject); void* result = gExceptionData->fWatchList->fObject; if (gExceptionData->fCurrentContext) DeleteStack_ClearHeapObjectElements(); gExceptionData->fWatchList->fObject = 0; gExceptionData->fWatchList = gExceptionData->fWatchList->fNext; return result; } void FW_PrivWatcher_Destroy(void* self) { #ifdef FW_DEBUG FW_AutoDestructed(self, FW_PrivWatcher_Destroy, gWatcherName); #else FW_AutoDestructed(self); #endif if (gExceptionData->fWatchList == self) { FW_SPrivWatcher* me = (FW_SPrivWatcher*) self; FW_PRIV_ASSERT(me->fObject); FW_PrimitiveFreeBlock(me->fObject); gExceptionData->fWatchList = me->fNext; } } void* FW_PrivWatcher_New(void* p, size_t size, FW_SPrivWatcher* self) { Watcher_Push(self, p, size); return p; } //======================================================================================== // Global Functions //======================================================================================== void FW_AutoConstructed(void* object, size_t, FW_PrivDestroyProc destroyer _FW_NAME_PARAMETER) { if (FW_gInStaticInit == kMagicCookie) return; FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(("Error: an autodestruct object constructed without an enclosing a FW_TRY block", gExceptionData->fCurrentContext!=NULL)); FW_PRIV_ASSERT(gExceptionData->fStackTop); FW_PRIV_ASSERT(gExceptionData->fStackBottom); if (TrackThisObject(object)) { #ifdef FW_DEBUG DeleteStack_Push(object, destroyer, name); #else DeleteStack_Push(object, destroyer); #endif } else { #ifdef FW_DEBUG char buffer[256]; sprintf(buffer, "Why is an autodestruct object of class %s being constructed in a bad context? See FWExcImp.cpp for possible causes.", name); FW_DEBUG_MESSAGE(buffer); // Possible causes: // 1) The autodestruct object is a member of another object which itself // is not an autodestruct object. This other object is being // allocated via new, not FW_NEW. If so, this is a programmer error. // Make the other object's class be autodestruct, and use FW_NEW. // 2) This is a static object of a shared library that has just been // loaded, and static constructors are being executed. We have attempted // to detect this condition and not report it as an error, but may not // have eliminated it for all environments. If this is the case, it // is not a programming error. You can safely continue execution. // Note: if you move this code into an application (as opposed to a // shared library), you may need to set FW_gInStaticInit to zero // in your main() to inform this subsystem that static initialization // is complete. // 3) A case similar to 2) is possible. If a static autodestruct object // is declared in a function, the compiler is not obligated to initialize // it at static initialization time, it can (and should) wait until the function is // first called. This case is very difficult to detect (without help // from the compiler!). It is not a programming error, and it is safe // to continue excecution. #endif } } void FW_AutoDestructed( void* object #ifdef FW_DEBUG , FW_PrivDestroyProc destroyer , char* name #endif ) { if (FW_gInStaticInit == kMagicCookie) return; FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fStackTop); FW_PRIV_ASSERT(gExceptionData->fStackBottom); if (gExceptionData->fCurrentContext) { if (TrackThisObject(object)) { FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1; FW_SPrivDeleteElem* last = gExceptionData->fStackBottom + gExceptionData->fCurrentContext->fDeleteStackLevel; // Search for the correct entry on the delete stack. // Normally this will be the first entry, but not always. while (elem>=last && elem->fObject!=object) --elem; #ifdef FW_DEBUG // If any of the following tests fail, we didn't find the object being destructed // in our delete stack. Probable cause is a FW_START_DESTRUCTOR without // matching FW_END_CONSTRUCTOR. char message[256]; if (elem<last) { sprintf(message, "The autodestruct class %s is missing an FW_END_CONSTRUCTOR.", name); FW_DEBUG_MESSAGE(message); } else if (elem->fObject != object || elem->fDestroyer != destroyer) { sprintf(message, "An autodestruct class is missing an FW_END_CONSTRUCTOR or FW_START_DESTRUCTOR. Likely candidates: %s and %s respectively.", name, elem->fName); FW_DEBUG_MESSAGE(message); } #endif // Objects aren't necessarily removed in the order they were pushed. // We simply clear entries on the stack, and then reset the stack top // to be one past the last active item on the stack. elem->fObject = 0; DeleteStack_ResetTop(); } } } void* FW_PrivGetThrownException() { return gExceptionData->fThrownException.fAddress; } void FW_PrivCaughtException(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner) { // We've caught an exception by value, and possibly sliced the object. // We like to not clear the fThrownException in case we do a FW_THROW_SAME. // Unfortunately, we don't work that way right now !!! •••JEL FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(!gExceptionData->fCaughtException.fAddress); ExceptionInfo_Set(gExceptionData->fCaughtException, exception, size, itsClass, itsDestroyer, itsCloner); ExceptionInfo_Clear(gExceptionData->fThrownException); } void FW_PrivCaughtNoInstanceException() { // We've caught an exception by name only FW_PRIV_ASSERT(gExceptionData); gExceptionData->fCaughtException = gExceptionData->fThrownException; ExceptionInfo_Clear(gExceptionData->fThrownException); } void FW_PrivCaughtEverythingException() { // We've caught any kind of exception FW_PrivCaughtNoInstanceException(); } void FW_PrivCaughtReferenceException() { // We've caught an exception by reference FW_PrivCaughtNoInstanceException(); } void FW_PrivCatchCleanup() { FW_PRIV_ASSERT(gExceptionData); ExceptionInfo_DestroyException(gExceptionData->fCaughtException); // The thrown exception already be cleared FW_PRIV_ASSERT(gExceptionData->fThrownException.fAddress == 0); } void FW_PrivKeepThrowing() { // An exception didn't match any of the Catch clauses, so it // needs to be propogated up to the next try block FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer); FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer == gExceptionData->fThrownException.fAddress); FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext; FW_PRIV_ASSERT(context != NULL); TryBlockContext_UnwindStack(context); TryBlockContext_LongJump(context); // LongJump doesn't return } FW_Boolean FW_PrivCanCatchThisException(FW_SClassInfoPtr targetClass) { FW_PRIV_ASSERT(gExceptionData); void* address = gExceptionData->fThrownException.fAddress; FW_PRIV_ASSERT(address); FW_SClassInfoPtr fromClass = gExceptionData->fThrownException.fClass; return 0 != FW_PrivDynamicCast(address, fromClass, fromClass, targetClass); } void FW_PrivThrow(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner) { FW_PRIV_ASSERT(exception); FW_PRIV_ASSERT(size); FW_PRIV_ASSERT(itsClass); FW_PRIV_ASSERT(itsDestroyer); FW_PRIV_ASSERT(itsCloner); FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer); FW_PRIV_ASSERT(gExceptionData->fExceptionBufferSize); FW_PRIV_ASSERT(size <= gExceptionData->fExceptionBufferSize); FW_PRIV_ASSERT(!gExceptionData->fIsUnwinding); if (gExceptionData->fIsUnwinding) { // Can't throw exception while unwinding stack from previous exception DoTerminate(); } // fThrownException is non-null only between throw and catch // If it is non-null on entry to PrivThrow then something is horribly wrong FW_PRIV_ASSERT(gExceptionData->fThrownException.fAddress == NULL); FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext; if (context == NULL) DoTerminate(); // If we're in a catch block and we throw then there // will be a caught exception that must be destroyed before // we write over it with the new exception. if (gExceptionData->fCaughtException.fAddress != NULL) ExceptionInfo_DestroyException(gExceptionData->fCaughtException); // Copy the original into our exception buffer (*itsCloner)(exception, gExceptionData->fExceptionBuffer, size); // We have copied the original so delete it. (*itsDestroyer)(exception); // We have a thrown exception so point gThrownException at it. ExceptionInfo_Set(gExceptionData->fThrownException, gExceptionData->fExceptionBuffer, size, itsClass, itsDestroyer, itsCloner); TryBlockContext_UnwindStack(context); TryBlockContext_LongJump(context); // doesn't return } void FW_PrivThrowSame() { FW_PRIV_ASSERT(gExceptionData); FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer); FW_PRIV_ASSERT(gExceptionData->fCaughtException.fAddress); if (gExceptionData->fCaughtException.fAddress == NULL) { // can't rethrow if no exception is in scope DoTerminate(); } FW_PRIV_ASSERT(!gExceptionData->fIsUnwinding); if (gExceptionData->fIsUnwinding) { // Can't throw exception while unwinding stack from previous exception DoTerminate(); } FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext; FW_PRIV_ASSERT(context != NULL); if (context == NULL) DoTerminate(); gExceptionData->fThrownException = gExceptionData->fCaughtException; ExceptionInfo_Clear(gExceptionData->fCaughtException); TryBlockContext_UnwindStack(context); TryBlockContext_LongJump(context); // LongJump doesn't return, we should never get to here FW_PRIV_ASSERT(false); } #endif // FW_NATIVE_EXCEPTIONS